/**
 * @file gauss.h
 *
 * The classes to implement Gaussian models
 *
 * @author Dahua Lin
 */

#ifdef _MSC_VER
#pragma once
#endif

#ifndef GMLPP_GAUSS_H_
#define GMLPP_GAUSS_H_

#include <gmlpp/model/model_base.h>
#include <gmlpp/core/pdmat.h>
#include <gmlpp/core/metrics.h>

namespace gml
{

	template<class CovType>
	class GaussModel_WithFixedCov
	{
	public:
		typedef CovType covariance_type;

	public:
		GaussModel_WithFixedCov(const CovType& C)
		: m_cov(C)
		, m_invcov(C.inv())
		{
		}

		const CovType& cov() const
		{
			return m_cov;
		}

		const CovType& inv_cov() const
		{
			return m_invcov;
		}

		uword dim() const
		{
			return m_cov.dim();
		}

	public:

	private:
		CovType m_cov;
		CovType m_invcov;

	}; // end class GaussModel_WithFixedCov



	template<class CovType>
	class GaussFactor_WithFixedCov : public FactorBase
	{
	public:
		typedef CovType covariance_type;
		typedef GaussModel_WithFixedCov<CovType> model_type;

	public:
		GaussFactor_WithFixedCov(const CovType& C)
		: m_model(C)
		{
		}

		const model_type& model() const
		{
			return m_model;
		}

		const CovType& cov() const
		{
			return m_model.cov();
		}

	public:
		// Factor: mean vector

		friend class GaussFactor_WithFixedCov::MeanVecSlot;

		class MeanVecSlot : public FactorSlotBase
		{
		private:
			friend class GaussFactor_WithFixedCov;

			MeanVecSlot(GaussFactor_WithFixedCov& G)
			: m_host(G), m_pvar(nullptr) { }

		public:
			virtual const FactorBase& host() const { return m_host; }
			virtual FactorBase& host() { return m_host; }

			virtual void set_var(const VariableBase& var)
			{
				m_pvar = dynamic_cast<VecVariable*>(&var);
			}

			virtual const VariableBase* ptr_var() const
			{
				return m_pvar;
			}

			virtual void on_var_updated()
			{
				m_host.on_meanvec_updated();
			}

		private:
			GaussFactor_WithFixedCov& m_host;
			const VecVariable *m_pvar;
		};


		// Factor: samples

		friend class GaussFactor_WithFixedCov::SampleSlot;

		class SampleSlot : public FactorSlotBase
		{
		private:
			friend class GaussFactor_WithFixedCov;

			SampleSlot(GaussFactor_WithFixedCov& G)
			: m_host(G), m_pvar(nullptr) { }

		public:
			virtual const FactorBase& host() const { return m_host; }
			virtual FactorBase& host() { return m_host; }

			virtual void set_var(const VariableBase& var)
			{
				m_pvar = dynamic_cast<VecVariable*>(&var);
			}

			virtual const VariableBase* ptr_var() const
			{
				return m_pvar;
			}

			virtual void on_var_updated()
			{
				m_host.on_sample_updated();
			}

		private:
			GaussFactor_WithFixedCov& m_host;
			const VecVariable *m_pvar;
		};


	private:

		void on_meanvec_updated()
		{
			m_meanvec_updated = true;
		}

		void on_sample_updated()
		{
			m_sample_updated = true;
		}


	private:
		model_type m_model;
		MeanVecSlot m_meanvec_slot;
		SampleSlot m_sample_slot;

		bool m_meanvec_updated;
		bool m_sample_updated;

	}; // end class GaussFactor_WithFixedCov


}

#endif /* GAUSS_H_ */
